package com.ruoyi.agriculture.knowledge.controller;

import com.ruoyi.common.annotation.Anonymous;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import org.springframework.web.bind.annotation.*;

import java.sql.*;
import java.util.*;

/**
 * 优化的统一知识图谱控制器 - 支持第四级展开和布局优化
 *
 * @author 宋瑞智
 */
@Anonymous
@RestController
@RequestMapping("/knowledge/unified")
public class UnifiedGraphController extends BaseController {

    // 数据库连接配置
    private static final String DB_URL = "jdbc:mysql://localhost:3306/agriculture?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai";
    private static final String DB_USER = "root";
    private static final String DB_PASSWORD = "root";

    // 🔥 每种类型显示的L3节点数量
    private static final int NODES_PER_TYPE = 3;

    /**
     * 🔥 获取统一知识图谱数据 - 默认展开到第三级，优化布局
     */
    @GetMapping("/data")
    public AjaxResult getUnifiedGraphData(@RequestParam(defaultValue = "crop_1") String centerNode) {
        Connection conn = null;
        try {
            Map<String, Object> result = new HashMap<>();
            conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);

            // 获取节点数据 - 默认展开到第三级，包含预设位置
            List<Map<String, Object>> nodes = getUnifiedGraphNodesWithOptimizedLayout(conn, centerNode);
            // 获取关系数据
            List<Map<String, Object>> links = getUnifiedGraphLinks(conn, centerNode);

            result.put("nodes", nodes);
            result.put("links", links);
            result.put("centerNode", centerNode);

            return success(result);
        } catch (Exception e) {
            e.printStackTrace();
            return error("获取统一知识图谱数据失败: " + e.getMessage());
        } finally {
            closeConnection(conn);
        }
    }
    /**
     * 🔥 新增：获取包含指定节点的知识图谱数据（用于详情页）
     */
    @GetMapping("/dataWithSpecificNode")
    public AjaxResult getGraphDataWithSpecificNode(
            @RequestParam(defaultValue = "crop_1") String centerNode,
            @RequestParam String specificNodeName,
            @RequestParam(required = false) String nodeType) {

        Connection conn = null;
        try {
            Map<String, Object> result = new HashMap<>();
            conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);

            // 🔥 1. 确定中心节点（根据specificNodeName判断是小麦还是玉米）
            String actualCenterNode = determineCenterNodeByName(conn, specificNodeName);
            if (actualCenterNode == null) {
                actualCenterNode = centerNode; // fallback到默认值
            }

            // 🔥 2. 获取包含指定节点的图谱数据
            List<Map<String, Object>> nodes = getGraphNodesWithSpecificNode(conn, actualCenterNode, specificNodeName, nodeType);
            List<Map<String, Object>> links = getUnifiedGraphLinks(conn, actualCenterNode);

            result.put("nodes", nodes);
            result.put("links", links);
            result.put("centerNode", actualCenterNode);
            result.put("specificNode", specificNodeName);

            System.out.println("生成包含指定节点的图谱: 中心=" + actualCenterNode + ", 指定节点=" + specificNodeName);

            return success(result);
        } catch (Exception e) {
            e.printStackTrace();
            return error("获取指定节点图谱数据失败: " + e.getMessage());
        } finally {
            closeConnection(conn);
        }
    }

    /**
     * 🔥 根据节点名称确定中心节点（小麦还是玉米）
     */
    private String determineCenterNodeByName(Connection conn, String nodeName) throws SQLException {
        PreparedStatement pstmt = null;
        ResultSet rs = null;

        try {
            // 查找该节点属于哪个作物
            String sql = "SELECT parent_id FROM zs_unified_knowledge_nodes " +
                    "WHERE node_name = ? AND status = 1 LIMIT 1";

            pstmt = conn.prepareStatement(sql);
            pstmt.setString(1, nodeName);
            rs = pstmt.executeQuery();

            if (rs.next()) {
                String parentId = rs.getString("parent_id");

                // 如果parent_id包含crop信息，直接返回
                if (parentId != null && parentId.startsWith("crop_")) {
                    return parentId;
                }

                // 否则继续向上查找
                return findRootCropNode(conn, parentId);
            }

            // 根据名称模糊匹配
            if (nodeName.contains("小麦")) {
                return "crop_1"; // 小麦
            } else if (nodeName.contains("玉米")) {
                return "crop_2"; // 玉米
            }

            return "crop_1"; // 默认小麦

        } finally {
            if (rs != null) rs.close();
            if (pstmt != null) pstmt.close();
        }
    }

    /**
     * 🔥 递归查找根作物节点
     */
    private String findRootCropNode(Connection conn, String nodeId) throws SQLException {
        if (nodeId == null || nodeId.startsWith("crop_")) {
            return nodeId;
        }

        PreparedStatement pstmt = null;
        ResultSet rs = null;

        try {
            String sql = "SELECT parent_id FROM zs_unified_knowledge_nodes " +
                    "WHERE node_id = ? AND status = 1";

            pstmt = conn.prepareStatement(sql);
            pstmt.setString(1, nodeId);
            rs = pstmt.executeQuery();

            if (rs.next()) {
                return findRootCropNode(conn, rs.getString("parent_id"));
            }

            return "crop_1"; // 默认

        } finally {
            if (rs != null) rs.close();
            if (pstmt != null) pstmt.close();
        }
    }

    /**
     * 🔥 获取包含指定节点的图谱节点数据
     */
    private List<Map<String, Object>> getGraphNodesWithSpecificNode(Connection conn, String centerNode,
                                                                    String specificNodeName, String nodeType) throws SQLException {

        List<Map<String, Object>> nodes = new ArrayList<>();
        PreparedStatement pstmt = null;
        ResultSet rs = null;

        try {
            // 🔥 第一步：获取L1作物节点
            String l1Sql = "SELECT node_id, node_name, node_type, category, level, description, parent_id " +
                    "FROM zs_unified_knowledge_nodes " +
                    "WHERE node_id = ? AND status = 1";
            pstmt = conn.prepareStatement(l1Sql);
            pstmt.setString(1, centerNode);
            rs = pstmt.executeQuery();

            while (rs.next()) {
                Map<String, Object> node = createNodeMap(rs);
                // 设置中心位置
                node.put("fx", 400.0);
                node.put("fy", 300.0);
                node.put("fixed", true);
                nodes.add(node);
            }
            rs.close();
            pstmt.close();

            // 🔥 第二步：获取L2类型节点
            String l2Sql = "SELECT node_id, node_name, node_type, category, level, description, parent_id " +
                    "FROM zs_unified_knowledge_nodes " +
                    "WHERE level = 2 AND parent_id = ? AND status = 1 " +
                    "ORDER BY node_type";
            pstmt = conn.prepareStatement(l2Sql);
            pstmt.setString(1, centerNode);
            rs = pstmt.executeQuery();

            List<String> typeNodeIds = new ArrayList<>();
            List<Map<String, Object>> l2Nodes = new ArrayList<>();

            while (rs.next()) {
                Map<String, Object> node = createNodeMap(rs);
                l2Nodes.add(node);
                typeNodeIds.add(rs.getString("node_id"));
            }
            rs.close();
            pstmt.close();

            // 设置L2节点环形布局
            setL2NodesLayout(l2Nodes);
            nodes.addAll(l2Nodes);

            // 🔥 第三步：为每个L2类型节点获取L3具体项，确保包含指定节点
            for (int typeIndex = 0; typeIndex < typeNodeIds.size(); typeIndex++) {
                String typeNodeId = typeNodeIds.get(typeIndex);

                List<Map<String, Object>> l3NodesForType = getL3NodesWithSpecific(
                        conn, typeNodeId, specificNodeName, nodeType, typeIndex, l2Nodes.get(typeIndex));

                nodes.addAll(l3NodesForType);
            }

        } finally {
            if (rs != null) rs.close();
            if (pstmt != null) pstmt.close();
        }

        return nodes;
    }

    /**
     * 🔥 获取L3节点，确保包含指定节点
     */
    private List<Map<String, Object>> getL3NodesWithSpecific(Connection conn, String typeNodeId,
                                                             String specificNodeName, String nodeType, int typeIndex, Map<String, Object> l2Node) throws SQLException {

        List<Map<String, Object>> l3Nodes = new ArrayList<>();
        PreparedStatement pstmt = null;
        ResultSet rs = null;

        try {
            // 🔥 首先尝试查找指定节点
            String specificSql = "SELECT node_id, node_name, node_type, category, level, description, parent_id " +
                    "FROM zs_unified_knowledge_nodes " +
                    "WHERE parent_id = ? AND node_name = ? AND status = 1";

            pstmt = conn.prepareStatement(specificSql);
            pstmt.setString(1, typeNodeId);
            pstmt.setString(2, specificNodeName);
            rs = pstmt.executeQuery();

            boolean foundSpecificNode = false;
            if (rs.next()) {
                Map<String, Object> specificNode = createNodeMap(rs);
                specificNode.put("isSpecific", true); // 标记为指定节点
                l3Nodes.add(specificNode);
                foundSpecificNode = true;
            }
            rs.close();
            pstmt.close();

            // 🔥 然后获取其他随机节点（总共3个，已有1个指定节点，还需要2个）
            int remainingCount = foundSpecificNode ? 2 : 3;

            String otherSql = "SELECT node_id, node_name, node_type, category, level, description, parent_id " +
                    "FROM zs_unified_knowledge_nodes " +
                    "WHERE parent_id = ? AND node_name != ? AND status = 1 " +
                    "ORDER BY RAND() LIMIT ?";

            pstmt = conn.prepareStatement(otherSql);
            pstmt.setString(1, typeNodeId);
            pstmt.setString(2, specificNodeName);
            pstmt.setInt(3, remainingCount);
            rs = pstmt.executeQuery();

            while (rs.next()) {
                Map<String, Object> node = createNodeMap(rs);
                node.put("hasChildren", hasChildNodes(conn, rs.getString("node_id")));
                node.put("expanded", false);
                l3Nodes.add(node);
            }
            rs.close();
            pstmt.close();

            // 🔥 设置L3节点的扇形布局
            setL3NodesLayout(l3Nodes, l2Node, typeIndex);

        } finally {
            if (rs != null) rs.close();
            if (pstmt != null) pstmt.close();
        }

        return l3Nodes;
    }

    /**
     * 🔥 创建节点映射
     */
    private Map<String, Object> createNodeMap(ResultSet rs) throws SQLException {
        Map<String, Object> node = new HashMap<>();
        node.put("id", rs.getString("node_id"));
        node.put("name", rs.getString("node_name"));
        node.put("type", rs.getString("node_type"));
        node.put("category", rs.getString("category"));
        node.put("level", rs.getInt("level"));
        node.put("description", rs.getString("description"));
        node.put("parent_id", rs.getString("parent_id"));
        return node;
    }

    /**
     * 🔥 设置L2节点环形布局
     */
    private void setL2NodesLayout(List<Map<String, Object>> l2Nodes) {
        double centerX = 400.0;
        double centerY = 300.0;
        double radius = 150.0;
        int l2Count = l2Nodes.size();

        for (int i = 0; i < l2Count; i++) {
            Map<String, Object> node = l2Nodes.get(i);
            double angle = (2 * Math.PI * i) / l2Count - Math.PI / 2;

            double x = centerX + radius * Math.cos(angle);
            double y = centerY + radius * Math.sin(angle);

            node.put("fx", x);
            node.put("fy", y);
            node.put("fixed", true);
        }
    }

    /**
     * 🔥 设置L3节点扇形布局
     */
    private void setL3NodesLayout(List<Map<String, Object>> l3Nodes, Map<String, Object> l2Node, int typeIndex) {
        if (l3Nodes.isEmpty()) return;

        double centerX = 400.0;
        double centerY = 300.0;
        double l2X = (Double) l2Node.get("fx");
        double l2Y = (Double) l2Node.get("fy");
        double l3Radius = 100.0;
        int l3Count = l3Nodes.size();

        for (int i = 0; i < l3Count; i++) {
            Map<String, Object> l3Node = l3Nodes.get(i);

            // 计算相对于L2节点的扇形角度
            double baseAngle = Math.atan2(l2Y - centerY, l2X - centerX);
            double spreadAngle = Math.PI / 3; // 60度扇形
            double startAngle = baseAngle - spreadAngle / 2;
            double angle = startAngle + (spreadAngle * i) / Math.max(1, l3Count - 1);

            double x = l2X + l3Radius * Math.cos(angle);
            double y = l2Y + l3Radius * Math.sin(angle);

            l3Node.put("fx", x);
            l3Node.put("fy", y);
            l3Node.put("fixed", true);

            // 🔥 如果是指定节点，添加特殊标记
            if (Boolean.TRUE.equals(l3Node.get("isSpecific"))) {
                l3Node.put("highlight", true);
            }
        }
    }

    /**
     * 🔥 新增：获取第四级子节点（品种特征）
     */
    @GetMapping("/children/{parentNodeId}")
    public AjaxResult getNodeChildren(@PathVariable String parentNodeId) {
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;

        try {
            conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);
            Map<String, Object> result = new HashMap<>();

            // 获取子节点数据
            String sql = "SELECT node_id, node_name, node_type, category, level, description, parent_id " +
                    "FROM zs_unified_knowledge_nodes " +
                    "WHERE parent_id = ? AND status = 1 " +
                    "ORDER BY node_name";

            pstmt = conn.prepareStatement(sql);
            pstmt.setString(1, parentNodeId);
            rs = pstmt.executeQuery();

            List<Map<String, Object>> children = new ArrayList<>();
            while (rs.next()) {
                Map<String, Object> node = new HashMap<>();
                node.put("id", rs.getString("node_id"));
                node.put("name", rs.getString("node_name"));
                node.put("type", rs.getString("node_type"));
                node.put("category", rs.getString("category"));
                node.put("level", rs.getInt("level"));
                node.put("description", rs.getString("description"));
                node.put("parent_id", rs.getString("parent_id"));
                children.add(node);
            }
            rs.close();
            pstmt.close();

            // 获取关系数据
            String linkSql = "SELECT source_node_id, target_node_id, relation_type, relation_label " +
                    "FROM zs_unified_knowledge_relations " +
                    "WHERE source_node_id = ? AND status = 1";

            pstmt = conn.prepareStatement(linkSql);
            pstmt.setString(1, parentNodeId);
            rs = pstmt.executeQuery();

            List<Map<String, Object>> links = new ArrayList<>();
            while (rs.next()) {
                Map<String, Object> link = new HashMap<>();
                link.put("source", rs.getString("source_node_id"));
                link.put("target", rs.getString("target_node_id"));
                link.put("type", rs.getString("relation_type"));
                link.put("label", rs.getString("relation_label"));
                links.add(link);
            }

            result.put("nodes", children);
            result.put("links", links);
            result.put("parentNodeId", parentNodeId);

            System.out.println("获取子节点，父节点: " + parentNodeId + ", 子节点数量: " + children.size());

            return success(result);
        } catch (Exception e) {
            e.printStackTrace();
            return error("获取子节点失败: " + e.getMessage());
        } finally {
            closeResources(rs, pstmt, conn);
        }
    }

    /**
     * 获取搜索建议
     */
    @GetMapping("/suggestions")
    public AjaxResult getUnifiedSearchSuggestions(@RequestParam String keyword) {
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;

        try {
            conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);
            List<Map<String, Object>> suggestions = new ArrayList<>();

            String sql = "SELECT node_id, node_name, node_type, category, level, description " +
                    "FROM zs_unified_knowledge_nodes " +
                    "WHERE node_name LIKE ? AND status = 1 " +
                    "ORDER BY level, node_name " +
                    "LIMIT 10";

            pstmt = conn.prepareStatement(sql);
            pstmt.setString(1, "%" + keyword + "%");
            rs = pstmt.executeQuery();

            while (rs.next()) {
                String nodeType = rs.getString("node_type");
                String description = getNodeTypeDescription(nodeType, rs.getString("category"));

                suggestions.add(createSuggestion(
                        rs.getString("node_id"),
                        rs.getString("node_name"),
                        nodeType,
                        description
                ));
            }

            return success(suggestions);
        } catch (Exception e) {
            e.printStackTrace();
            return error("获取搜索建议失败: " + e.getMessage());
        } finally {
            closeResources(rs, pstmt, conn);
        }
    }

    /**
     * 搜索节点
     */
    @GetMapping("/search")
    public AjaxResult searchUnifiedNodes(@RequestParam String keyword, @RequestParam(required = false) String nodeId) {
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;

        try {
            conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);

            String centerNode = null;
            String searchType = "crop";

            if (nodeId != null && !nodeId.trim().isEmpty()) {
                centerNode = nodeId;
                searchType = determineNodeType(nodeId);
            } else {
                String sql = "SELECT node_id, node_name, node_type " +
                        "FROM zs_unified_knowledge_nodes " +
                        "WHERE node_name LIKE ? AND status = 1 " +
                        "ORDER BY level " +
                        "LIMIT 1";

                pstmt = conn.prepareStatement(sql);
                pstmt.setString(1, "%" + keyword + "%");
                rs = pstmt.executeQuery();

                if (rs.next()) {
                    centerNode = rs.getString("node_id");
                    searchType = rs.getString("node_type");
                }
            }

            if (centerNode != null) {
                Map<String, Object> result = new HashMap<>();
                result.put("centerNode", centerNode);
                result.put("keyword", keyword);
                result.put("searchType", searchType);
                return success(result);
            } else {
                return error("未找到相关节点，请搜索作物名称、品种名称或病虫害名称");
            }

        } catch (Exception e) {
            e.printStackTrace();
            return error("搜索失败: " + e.getMessage());
        } finally {
            closeResources(rs, pstmt, conn);
        }
    }

    /**
     * 获取节点详情
     */
    @GetMapping("/node/{nodeId}")
    public AjaxResult getNodeDetail(@PathVariable String nodeId) {
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;

        try {
            conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);

            String sql = "SELECT node_id, node_name, node_type, category, level, description, " +
                    "variety_info, resistance_info, symptoms, occurrence_condition, control_methods " +
                    "FROM zs_unified_knowledge_nodes WHERE node_id = ? AND status = 1";

            Map<String, Object> result = new HashMap<>();

            pstmt = conn.prepareStatement(sql);
            pstmt.setString(1, nodeId);
            rs = pstmt.executeQuery();

            if (rs.next()) {
                result.put("nodeId", rs.getString("node_id"));
                result.put("nodeName", rs.getString("node_name"));
                result.put("nodeType", rs.getString("node_type"));
                result.put("category", rs.getString("category"));
                result.put("level", rs.getInt("level"));
                result.put("description", rs.getString("description"));
                result.put("varietyInfo", rs.getString("variety_info"));
                result.put("resistanceInfo", rs.getString("resistance_info"));
                result.put("symptoms", rs.getString("symptoms"));
                result.put("occurrenceCondition", rs.getString("occurrence_condition"));
                result.put("controlMethods", rs.getString("control_methods"));
            }

            return success(result);
        } catch (Exception e) {
            return error("获取节点详情失败: " + e.getMessage());
        } finally {
            closeResources(rs, pstmt, conn);
        }
    }

    // =================================
    // 私有方法实现
    // =================================

    /**
     * 🔥 优化版：获取具有预设布局位置的图谱节点
     */
    private List<Map<String, Object>> getUnifiedGraphNodesWithOptimizedLayout(Connection conn, String centerNode) throws SQLException {
        List<Map<String, Object>> nodes = new ArrayList<>();
        PreparedStatement pstmt = null;
        ResultSet rs = null;

        try {
            // 🔥 第一步：获取L1作物节点 - 设置中心位置
            String l1Sql = "SELECT node_id, node_name, node_type, category, level, description, parent_id " +
                    "FROM zs_unified_knowledge_nodes " +
                    "WHERE node_id = ? AND status = 1";
            pstmt = conn.prepareStatement(l1Sql);
            pstmt.setString(1, centerNode);
            rs = pstmt.executeQuery();

            while (rs.next()) {
                Map<String, Object> node = new HashMap<>();
                node.put("id", rs.getString("node_id"));
                node.put("name", rs.getString("node_name"));
                node.put("type", rs.getString("node_type"));
                node.put("category", rs.getString("category"));
                node.put("level", rs.getInt("level"));
                node.put("description", rs.getString("description"));
                node.put("parent_id", rs.getString("parent_id"));

                // 🔥 预设L1节点位置（图谱中心）
                node.put("fx", 400.0); // 固定X坐标
                node.put("fy", 300.0); // 固定Y坐标
                node.put("fixed", true); // 标记为固定位置

                nodes.add(node);
            }
            rs.close();
            pstmt.close();

            // 🔥 第二步：获取L2类型节点 - 设置环形布局
            String l2Sql = "SELECT node_id, node_name, node_type, category, level, description, parent_id " +
                    "FROM zs_unified_knowledge_nodes " +
                    "WHERE level = 2 AND parent_id = ? AND status = 1 " +
                    "ORDER BY node_type";
            pstmt = conn.prepareStatement(l2Sql);
            pstmt.setString(1, centerNode);
            rs = pstmt.executeQuery();

            List<String> typeNodeIds = new ArrayList<>();
            List<Map<String, Object>> l2Nodes = new ArrayList<>();

            while (rs.next()) {
                Map<String, Object> node = new HashMap<>();
                node.put("id", rs.getString("node_id"));
                node.put("name", rs.getString("node_name"));
                node.put("type", rs.getString("node_type"));
                node.put("category", rs.getString("category"));
                node.put("level", rs.getInt("level"));
                node.put("description", rs.getString("description"));
                node.put("parent_id", rs.getString("parent_id"));

                l2Nodes.add(node);
                typeNodeIds.add(rs.getString("node_id"));
            }
            rs.close();
            pstmt.close();

            // 🔥 为L2节点设置环形布局
            double centerX = 400.0;
            double centerY = 300.0;
            double radius = 150.0;
            int l2Count = l2Nodes.size();

            for (int i = 0; i < l2Count; i++) {
                Map<String, Object> node = l2Nodes.get(i);
                double angle = (2 * Math.PI * i) / l2Count - Math.PI / 2; // 从上方开始

                double x = centerX + radius * Math.cos(angle);
                double y = centerY + radius * Math.sin(angle);

                node.put("fx", x);
                node.put("fy", y);
                node.put("fixed", true);
            }

            nodes.addAll(l2Nodes);

            // 🔥 第三步：为每个L2类型节点获取L3具体项 - 设置扇形布局
            for (int typeIndex = 0; typeIndex < typeNodeIds.size(); typeIndex++) {
                String typeNodeId = typeNodeIds.get(typeIndex);

                String l3Sql = "SELECT node_id, node_name, node_type, category, level, description, parent_id " +
                        "FROM zs_unified_knowledge_nodes " +
                        "WHERE parent_id = ? AND status = 1 " +
                        "ORDER BY RAND() LIMIT ?";
                pstmt = conn.prepareStatement(l3Sql);
                pstmt.setString(1, typeNodeId);
                pstmt.setInt(2, NODES_PER_TYPE);
                rs = pstmt.executeQuery();

                List<Map<String, Object>> l3NodesForType = new ArrayList<>();
                while (rs.next()) {
                    Map<String, Object> node = new HashMap<>();
                    node.put("id", rs.getString("node_id"));
                    node.put("name", rs.getString("node_name"));
                    node.put("type", rs.getString("node_type"));
                    node.put("category", rs.getString("category"));
                    node.put("level", rs.getInt("level"));
                    node.put("description", rs.getString("description"));
                    node.put("parent_id", rs.getString("parent_id"));

                    // 🔥 检查是否有子节点（第四级）
                    boolean hasChildren = hasChildNodes(conn, rs.getString("node_id"));
                    node.put("hasChildren", hasChildren);
                    node.put("expanded", false); // 初始为未展开状态

                    l3NodesForType.add(node);
                }
                rs.close();
                pstmt.close();

                // 🔥 为当前类型的L3节点设置扇形布局
                Map<String, Object> l2Node = l2Nodes.get(typeIndex);
                double l2X = (Double) l2Node.get("fx");
                double l2Y = (Double) l2Node.get("fy");

                double l3Radius = 100.0;
                int l3Count = l3NodesForType.size();

                for (int i = 0; i < l3Count; i++) {
                    Map<String, Object> l3Node = l3NodesForType.get(i);

                    // 计算相对于L2节点的扇形角度
                    double baseAngle = Math.atan2(l2Y - centerY, l2X - centerX);
                    double spreadAngle = Math.PI / 3; // 60度扇形
                    double startAngle = baseAngle - spreadAngle / 2;
                    double angle = startAngle + (spreadAngle * i) / Math.max(1, l3Count - 1);

                    double x = l2X + l3Radius * Math.cos(angle);
                    double y = l2Y + l3Radius * Math.sin(angle);

                    l3Node.put("fx", x);
                    l3Node.put("fy", y);
                    l3Node.put("fixed", true);
                }

                nodes.addAll(l3NodesForType);
            }

            System.out.println("优化布局完成，节点数量: " + nodes.size() + ", 中心节点: " + centerNode);

            // 按层级统计
            Map<Integer, Long> levelCount = nodes.stream()
                    .collect(java.util.stream.Collectors.groupingBy(
                            node -> (Integer) node.get("level"),
                            java.util.stream.Collectors.counting()
                    ));
            System.out.println("节点层级分布: " + levelCount);

        } finally {
            if (rs != null) rs.close();
            if (pstmt != null) pstmt.close();
        }

        return nodes;
    }

    /**
     * 🔥 新增：检查节点是否有子节点
     */
    private boolean hasChildNodes(Connection conn, String nodeId) throws SQLException {
        String sql = "SELECT COUNT(*) as count FROM zs_unified_knowledge_nodes WHERE parent_id = ? AND status = 1";
        try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
            pstmt.setString(1, nodeId);
            try (ResultSet rs = pstmt.executeQuery()) {
                if (rs.next()) {
                    return rs.getInt("count") > 0;
                }
            }
        }
        return false;
    }

    /**
     * 获取统一图谱关系数据
     */
    private List<Map<String, Object>> getUnifiedGraphLinks(Connection conn, String centerNode) throws SQLException {
        List<Map<String, Object>> links = new ArrayList<>();
        PreparedStatement pstmt = null;
        ResultSet rs = null;

        try {
            // 第一步：L1作物 -> L2类型的关系
            String l1ToL2Sql = "SELECT DISTINCT r.source_node_id, r.target_node_id, r.relation_type, r.relation_label " +
                    "FROM zs_unified_knowledge_relations r " +
                    "WHERE r.status = 1 " +
                    "  AND r.source_node_id = ? " +
                    "  AND r.target_node_id IN (" +
                    "    SELECT node_id FROM zs_unified_knowledge_nodes " +
                    "    WHERE level = 2 AND parent_id = ? AND status = 1" +
                    "  )";

            pstmt = conn.prepareStatement(l1ToL2Sql);
            pstmt.setString(1, centerNode);
            pstmt.setString(2, centerNode);
            rs = pstmt.executeQuery();

            while (rs.next()) {
                Map<String, Object> link = new HashMap<>();
                link.put("source", rs.getString("source_node_id"));
                link.put("target", rs.getString("target_node_id"));
                link.put("type", rs.getString("relation_type"));
                link.put("label", rs.getString("relation_label"));
                links.add(link);
            }
            rs.close();
            pstmt.close();

            // 第二步：L2类型 -> L3具体项的关系
            String l2ToL3Sql = "SELECT DISTINCT r.source_node_id, r.target_node_id, r.relation_type, r.relation_label " +
                    "FROM zs_unified_knowledge_relations r " +
                    "WHERE r.status = 1 " +
                    "  AND r.source_node_id IN (" +
                    "    SELECT node_id FROM zs_unified_knowledge_nodes " +
                    "    WHERE level = 2 AND parent_id = ? AND status = 1" +
                    "  ) " +
                    "  AND r.target_node_id IN (" +
                    "    SELECT node_id FROM zs_unified_knowledge_nodes " +
                    "    WHERE level = 3 AND parent_id IN (" +
                    "      SELECT node_id FROM zs_unified_knowledge_nodes " +
                    "      WHERE level = 2 AND parent_id = ? AND status = 1" +
                    "    ) AND status = 1" +
                    "  )";

            pstmt = conn.prepareStatement(l2ToL3Sql);
            pstmt.setString(1, centerNode);
            pstmt.setString(2, centerNode);
            rs = pstmt.executeQuery();

            while (rs.next()) {
                Map<String, Object> link = new HashMap<>();
                link.put("source", rs.getString("source_node_id"));
                link.put("target", rs.getString("target_node_id"));
                link.put("type", rs.getString("relation_type"));
                link.put("label", rs.getString("relation_label"));
                links.add(link);
            }
            rs.close();
            pstmt.close();

            System.out.println("层级关系查询完成，关系数量: " + links.size());

            // 如果关系数据不足，创建基础的层级关系
            if (links.size() < 5) {
                System.out.println("关系数据不足，创建基础层级关系...");
                links.addAll(createBasicHierarchyLinks(conn, centerNode));
            }

        } finally {
            if (rs != null) rs.close();
            if (pstmt != null) pstmt.close();
        }

        return links;
    }

    /**
     * 创建基础的层级关系（当数据库关系不完整时）
     */
    private List<Map<String, Object>> createBasicHierarchyLinks(Connection conn, String centerNode) throws SQLException {
        List<Map<String, Object>> basicLinks = new ArrayList<>();
        PreparedStatement pstmt = null;
        ResultSet rs = null;

        try {
            // 创建L1 -> L2的关系
            String l2NodesSql = "SELECT node_id FROM zs_unified_knowledge_nodes " +
                    "WHERE level = 2 AND parent_id = ? AND status = 1";
            pstmt = conn.prepareStatement(l2NodesSql);
            pstmt.setString(1, centerNode);
            rs = pstmt.executeQuery();

            while (rs.next()) {
                Map<String, Object> link = new HashMap<>();
                link.put("source", centerNode);
                link.put("target", rs.getString("node_id"));
                link.put("type", "包含");
                link.put("label", "包含");
                basicLinks.add(link);
            }
            rs.close();
            pstmt.close();

            // 创建L2 -> L3的关系
            String l3NodesSql = "SELECT n3.node_id, n3.parent_id " +
                    "FROM zs_unified_knowledge_nodes n3 " +
                    "WHERE n3.level = 3 " +
                    "  AND n3.parent_id IN (" +
                    "    SELECT node_id FROM zs_unified_knowledge_nodes " +
                    "    WHERE level = 2 AND parent_id = ? AND status = 1" +
                    "  ) " +
                    "  AND n3.status = 1";
            pstmt = conn.prepareStatement(l3NodesSql);
            pstmt.setString(1, centerNode);
            rs = pstmt.executeQuery();

            while (rs.next()) {
                Map<String, Object> link = new HashMap<>();
                link.put("source", rs.getString("parent_id"));
                link.put("target", rs.getString("node_id"));
                link.put("type", "包含");
                link.put("label", "包含");
                basicLinks.add(link);
            }
            rs.close();
            pstmt.close();

            System.out.println("创建基础关系数量: " + basicLinks.size());

        } finally {
            if (rs != null) rs.close();
            if (pstmt != null) pstmt.close();
        }

        return basicLinks;
    }

    // =================================
    // 辅助方法
    // =================================

    private Map<String, Object> createSuggestion(String id, String name, String type, String description) {
        Map<String, Object> suggestion = new HashMap<>();
        suggestion.put("id", id);
        suggestion.put("name", name);
        suggestion.put("type", type);
        suggestion.put("description", description);
        return suggestion;
    }

    private String getNodeTypeDescription(String nodeType, String category) {
        switch (nodeType) {
            case "crop": return "作物";
            case "variety_type": return "品种类型";
            case "disease_type": return "病害类型";
            case "pest_type": return "虫害类型";
            case "stress_type": return "胁迫类型";
            case "variety": return "品种";
            case "disease": return "病害";
            case "pest": return "虫害";
            case "stress": return "非生物胁迫";
            case "feature": return "特征";
            default: return category != null ? category : "未知类型";
        }
    }

    private String determineNodeType(String nodeId) {
        if (nodeId.startsWith("crop_")) return "crop";
        if (nodeId.startsWith("type_variety_")) return "variety_type";
        if (nodeId.startsWith("type_disease_")) return "disease_type";
        if (nodeId.startsWith("type_pest_")) return "pest_type";
        if (nodeId.startsWith("type_stress_")) return "stress_type";
        if (nodeId.startsWith("variety_")) return "variety";
        if (nodeId.startsWith("disease_")) return "disease";
        if (nodeId.startsWith("pest_")) return "pest";
        if (nodeId.startsWith("stress_")) return "stress";
        if (nodeId.startsWith("feature_")) return "feature";
        return "unknown";
    }

    private void closeConnection(Connection conn) {
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    private void closeResources(ResultSet rs, PreparedStatement pstmt, Connection conn) {
        try {
            if (rs != null) rs.close();
            if (pstmt != null) pstmt.close();
            if (conn != null) conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}